#!/usr/bin/env perl

$TCPDUMP = "./tcpdump";
if ($^O eq 'MSWin32') {
    $TCPDUMP = "windump";
}
if($ENV{TCPDUMP_BIN}) {
    $TCPDUMP = $ENV{TCPDUMP_BIN};
}

use File::Basename;
use POSIX qw( WEXITSTATUS WIFEXITED);

system("mkdir -p tests/NEW tests/DIFF");

if(@ARGV != 4) {
  print "Usage: TESTonce name input output options\n";
  exit 20;
}

$name=$ARGV[0];
$input=$ARGV[1];
$output=$ARGV[2];
$options=$ARGV[3];

my $r;
my $debug = 0; # change to suit.

$outputbase = basename($output);
my $coredump = false;
my $status = 0;
my $linecount = 0;
my $rawstderrlog = "tests/NEW/${outputbase}.raw.stderr";
my $stderrlog = "tests/NEW/${outputbase}.stderr";
my $diffstat = 0;
my $errdiffstat = 0;
my $cmd;

if ($^O eq 'MSWin32') {
    $r = system ".\\${TCPDUMP} -t -n -r $input $options 2>NUL | sed 's/\\r//' | tee tests/NEW/${outputbase} | diff $output - >tests/DIFF/${outputbase}.diff";
    # need to do same as below for Cygwin.
}
else {
    # we used to do this as a nice pipeline, but the problem is that $r fails to
    # to be set properly if the tcpdump core dumps.
    $cmd = "$TCPDUMP 2>${rawstderrlog} -t -n -r $input $options >tests/NEW/${outputbase}";
    print "CMD: $cmd\n" if $debug;
    $r = system $cmd;
    if($r == -1) {
        # failed to start due to error.
        $status = $!;
    }
    if($r != 0) {
        $coredump = false;
        $status = 0;
        # this means tcpdump failed, which however, might be expected.
        open(OUTPUT, ">>"."tests/NEW/${outputbase}") || die "fail to open ${outputbase}\n";
        if( $r & 128 ) {
            $coredump = $r & 127;
        }
        if( WIFEXITED($r)) {
            $status = WEXITSTATUS($r);
        }

        if($coredump || $status) {
            printf OUTPUT "\nEXIT CODE %08x: dump:%d code: %d\n", $r, $coredump, $status;
        } else {
            printf OUTPUT "\nEXIT CODE %08x\n", $r;
        }
        close(OUTPUT);
        $r = 0;  # clear the error so that the diff will occur.
    }
    print "RUNNING DIFF after ${r}\n" if $debug;

    # always run diff.
    $cmd = "cat tests/NEW/${outputbase} | diff $output - >tests/DIFF/${outputbase}.diff";
    print "RUNNING: $cmd\n" if $debug;
    $r = system $cmd;
    if(WIFEXITED($r)) {
        $diffstat = WEXITSTATUS($r);
    }

    #system("/bin/sh");

    # process the file, sanitize "reading from" line, and count lines
    $linecount = 0;
    open(ERRORRAW, "<" . $rawstderrlog);
    open(ERROROUT, ">" . $stderrlog);
    while(<ERRORRAW>) {
        next if /^\s*$/;  # blank lines are boring
        if(/^(reading from file )(.*)(,.*)$/) {
            my $filename = basename($2);
            print ERROROUT "${1}${filename}${3}\n";
            next;
        }
        print ERROROUT;
        $linecount++;
    }
    close(ERROROUT);
    close(ERRORRAW);

    if ( -f "$output.stderr" ) {
        $nr = system "cat $stderrlog | diff $output.stderr - >tests/DIFF/$outputbase.stderr.diff";
        if($r == 0) {
            $r = $nr;
        }
        $errdiffstat = WEXITSTATUS($nr);
    } else {
        $errdiffstat = "-"
    }

    if($r == 0) {
        if($linecount == 0 && $status == 0) {
            print "UNLINK: ${stderrlog}\n" if $debug;
            unlink($stderrlog) unless $debug;
        } else {
            $errdiffstat = "+";
        }
    }

    print sprintf("END: %08x\n", $r) if $debug;
}

if($r == 0) {
    my $stderrlog="";
    if($linecount > 0 && $errdiffstat != "-") {
        $stderrlog=sprintf("-- %d lines extra in stderr", $linecount);
    }
    if(!defined($ENV{"SKIPPASSED"})) {
        printf "    %-35s: passed%s\n", $name, $stderrlog;
    }
    unlink "tests/DIFF/$outputbase.diff" unless $debug;
    exit 0;
}
# must have failed!
printf "    %-35s: TEST FAILED(exit core=%d/diffstat=%d,%d/r=%d)", $name, $coredump, $diffstat, $errdiffstat, $r;
open FOUT, '>>tests/failure-outputs.txt';
printf FOUT "\nFailed test: $name\n\n";
close FOUT;
if(-f "tests/DIFF/$outputbase.diff") {
    system "cat tests/DIFF/$outputbase.diff >> tests/failure-outputs.txt";
}

if($r == -1) {
  print " (failed to execute: $!)\n";
  exit 30;
}

# this is not working right, $r == 0x8b00 when there is a core dump.
# clearly, we need some platform specific perl magic to take this apart, so look for "core"
# too.
# In particular, on Solaris 10 SPARC an alignment problem results in SIGILL,
# a core dump and $r set to 0x00008a00 ($? == 138 in the shell).
if($r & 127 || -f "core") {
    my $with = ($r & 128) ? 'with' : 'without';
    if(-f "core") {
        $with = "with";
    }
    printf " (terminated with signal %u, %s coredump)\n", ($r & 127), $with;
    exit ($r & 128) ? 10 : 20;
}
print "\n";
exit $r >> 8;
